<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图像处理</title>
    <style>
        canvas {
            border: 1px solid black;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <h1>图片灰度化和抖动处理，生成数据文件</h1>
    <input type="file" id="upload" accept="image/*">
    <br>
    <label for="threshold">阈值 (0-255): </label>
    <input type="range" id="threshold" min="0" max="255" value="128">
    <br>
    <label for="ditherLevel">抖动强度 (0-10): </label>
    <input type="range" id="ditherLevel" min="0" max="10" value="5">
    <br>
    <button id="process">处理图片</button>
    <button id="download" style="display:none;">下载图片和生成头文件</button>
    <br>
    <canvas id="originalCanvas"></canvas>
    <canvas id="processedCanvas"></canvas>

    <script>
        const upload = document.getElementById('upload');
        const thresholdInput = document.getElementById('threshold');
        const ditherLevelInput = document.getElementById('ditherLevel');
        const processButton = document.getElementById('process');
        const downloadButton = document.getElementById('download');
        const originalCanvas = document.getElementById('originalCanvas');
        const processedCanvas = document.getElementById('processedCanvas');
        const originalCtx = originalCanvas.getContext('2d');
        const processedCtx = processedCanvas.getContext('2d');

        let originalImage = new Image();

        upload.addEventListener('change', (event) => {
            const file = event.target.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = (e) => {
                    originalImage.src = e.target.result;
                };
                reader.readAsDataURL(file);
            }
        });

        originalImage.onload = () => {
            originalCanvas.width = originalImage.width;
            originalCanvas.height = originalImage.height;
            originalCtx.drawImage(originalImage, 0, 0);
        };

        function grayscale(imageData) {
            const data = imageData.data;
            for (let i = 0; i < data.length; i += 4) {
                const gray = data[i] * 0.3 + data[i + 1] * 0.59 + data[i + 2] * 0.11;
                data[i] = data[i + 1] = data[i + 2] = gray;
            }
        }

        function applyDithering(imageData, threshold, ditherLevel) {
            const data = imageData.data;
            const width = imageData.width;
            const height = imageData.height;
            const ditherMatrix = [
                [0, 8, 2, 10],
                [12, 4, 14, 6],
                [3, 11, 1, 9],
                [15, 7, 13, 5]
            ];

            for (let y = 0; y < height; y++) {
                for (let x = 0; x < width; x++) {
                    const index = (y * width + x) * 4;
                    let oldGray = data[index];
                    const newGray = oldGray < threshold ? 0 : 255;
                    const error = (oldGray - newGray) / ditherLevel;

                    data[index] = data[index + 1] = data[index + 2] = newGray;

                    const ditherIndex = (y % 4) * 4 + (x % 4);
                    const quantError = error * (ditherMatrix[Math.floor(ditherIndex / 4)][ditherIndex % 4] / 16);

                    if (x + 1 < width) {
                        data[(y * width + (x + 1)) * 4] += quantError;
                        data[(y * width + (x + 1)) * 4 + 1] += quantError;
                        data[(y * width + (x + 1)) * 4 + 2] += quantError;
                    }
                    if (y + 1 < height) {
                        data[((y + 1) * width + x) * 4] += quantError;
                        data[((y + 1) * width + x) * 4 + 1] += quantError;
                        data[((y + 1) * width + x) * 4 + 2] += quantError;
                    }
                    if (x + 1 < width && y + 1 < height) {
                        data[((y + 1) * width + (x + 1)) * 4] += quantError;
                        data[((y + 1) * width + (x + 1)) * 4 + 1] += quantError;
                        data[((y + 1) * width + (x + 1)) * 4 + 2] += quantError;
                    }
                }
            }
        }

        function generateHeaderFile(imageData, width, height) {
            const data = imageData.data;
            const bytesPerRow = Math.ceil(width / 8);
            const bitData = [];
            for (let y = 0; y < height; y++) {
                let byte = 0;
                let bitCount = 0;
                for (let x = 0; x < width; x++) {
                    const index = (y * width + x) * 4;
                    const gray = data[index] > 128 ? 1 : 0;
					const invertedBit = gray === 0 ? 1 : 0; // Invert for desired representation
                    byte = (byte << 1) | invertedBit;
                    bitCount++;
                    if (bitCount === 8 || x === width - 1) {
                        bitData.push(byte);
                        byte = 0;
                        bitCount = 0;
                    }
                }
                // Padding to ensure each row has the same number of bytes
                while (bitData.length % (bytesPerRow * 16 / 8) !== 0 && bitData.length % bytesPerRow !== 0) {
                    bitData.push(0);
                }
            }

            let headerContent = `#define image_width ${width}\n`;
            headerContent += `#define image_height ${height}\n\n`;
            headerContent += `static uint8_t image_bits[] = {\n`;
            for (let i = 0; i < bitData.length; i += 16) {
                headerContent += '    ';
                for (let j = i; j < i + 16 && j < bitData.length; j++) {
                    headerContent += `0x${bitData[j].toString(16).padStart(2, '0')}`;
					if (j < i + 15 && j < bitData.length - 1) {
                        headerContent += ', ';
                    }
                }
                headerContent += ',\n'; // Add comma for all lines except potentially the last one
            }
            // Remove the last comma if it's not needed
            headerContent = headerContent.trimEnd().replace(/,\n$/, '\n');
            headerContent += `};\n`;

            const blob = new Blob([headerContent], { type: 'text/plain' });
            const link = document.createElement('a');
            link.download = 'image.h';
            link.href = URL.createObjectURL(blob);
            link.click();
        }

        processButton.addEventListener('click', () => {
            const threshold = parseInt(thresholdInput.value, 10);
            const ditherLevel = parseInt(ditherLevelInput.value, 10) + 1; // Avoid division by zero

            processedCanvas.width = originalCanvas.width;
            processedCanvas.height = originalCanvas.height;
            const processedImageData = originalCtx.getImageData(0, 0, originalCanvas.width, originalCanvas.height);

            grayscale(processedImageData);
            applyDithering(processedImageData, threshold, ditherLevel);

            processedCtx.putImageData(processedImageData, 0, 0);
            downloadButton.style.display = 'inline';
        });

        downloadButton.addEventListener('click', () => {
            const processedImageData = processedCtx.getImageData(0, 0, processedCanvas.width, processedCanvas.height);
            const dataUrl = processedCanvas.toDataURL();
            const link = document.createElement('a');
            link.download = 'processed_image.png';
            link.href = dataUrl;
            link.click();

            generateHeaderFile(processedImageData, processedCanvas.width, processedCanvas.height);
        });
    </script>
</body>
</html>
